# coding=utf-8

# ansible special
from __future__ import absolute_import, division, print_function

__metaclass__ = type

from sshtunnel import SSHTunnelForwarder
from redis import StrictRedis

# noinspection PyUnresolvedReferences
from ansible.module_utils.basic import AnsibleModule

ANSIBLE_METADATA = {
    'metadata_version': 0.2,
    'status': 'beta',
    'supported_by': 'Yu Zhang'
}

DOCUMENTATION = r'''
---

module: redis_executor
short_description: Redis 执行器。
description:
  - 通过 ssh 通道连接至目标 redis 实例并执行给定命令。
options:
  ssh_ip:
    description:
      - ssh 隧道 IP 地址，数据库连接一律走 ssh 隧道确保安全性。
    required: true
    type: str
    no_log: false
  ssh_port:
    description:
      - ssh 端口号。
    required: false
    type: int
    default: 22
    no_log: false
  ssh_user:
    description:
      - ssh 认证用户名。
    required: true
    type: str
    no_log: false
  ssh_password:
    description:
      - ssh 认证密码。
    required: true
    type: str
    no_log: true
  ip:
    description:
      - redis 连接 IP。
    required: true
    type: str
    no_log: false
  port:
    description:
      - redis 连接 port。
    required: true
    type: int
    no_log: false
  user:
    description:
      - redis 连接用户名（redis 6.0+ acl new feature）。
    required: false
    type: str
    default: ''
    no_log: false
  password:
    description:
      - redis 连接密码。
    required: true
    type: str
    no_log: true
  db:
    description:
      - redis 连接 db。
    required: false
    type: int
    default: 0
    no_log: false
  cmmds:
    description:
      - redis 待执行命令。
    required: false
    type: str
    default: 'ping'
    no_log: false
  if_auth:
    description:
      - 是否通过 redis 认证连接实例：yes - 启用，no - 禁用。
    required: false
    type: str
    default: 'yes'
    no_log: false
  timeout:
    description:
      - 超时时间（秒）。
    required: false
    type: int
    default: 60
    no_log: false
author: "Yu Zhang"
requirements:
   - sshtunnel
   - redis
notes:
   - Requires the sshtunnel,redis library on the local host.
'''

EXAMPLES = r'''
- name: "redis_executor test"
  linora.redis.redis_executor:
    ssh_ip: "{{ ansible_ssh_host }}"
    ssh_port: "{{ ansible_ssh_port }}"
    ssh_user: "{{ ansible_ssh_user }}"
    ssh_password: "{{ ansible_password }}"
    ip: "{{ ansible_ssh_host }}"
    port: "{{ c_redis_port }}"
    user: "{{ c_redis_user }}"
    password: "{{ r_crypt_dict.c_redis_pass }}"
    db: 0
    if_auth: "{{ c_if_redis_auth }}"
  delegate_to: localhost
'''

RETURN = r'''
---
message:
    description: 返回命令执行结果，如返回：字符串、列表、set 等等。
    returned: changed
    type: str
    sample: abc
'''


def run_module():
    module_args = dict(
        # os section
        ssh_ip=dict(type='str', required=True),
        ssh_port=dict(type='int', required=False, default=22),
        ssh_user=dict(type='str', required=True),
        ssh_password=dict(type='str', required=True, no_log=True),
        # redis section
        ip=dict(type='str', required=True),
        port=dict(type='int', required=True),
        user=dict(type='str', required=False, default=''),
        password=dict(type='str', required=True, no_log=True),
        db=dict(type='int', required=False, default=0),
        cmmds=dict(type='str', required=False, default='ping'),
        if_auth=dict(type='str', required=False, default='yes'),
        timeout=dict(type='int', required=False, default=60)
    )

    ansible_session_flag = 'ansible_session'
    result = dict(
        changed=False,
        message=''
    )

    module = AnsibleModule(
        argument_spec=module_args,
        supports_check_mode=True
    )

    # 支持checkmode（检查模式，不运行模块）
    if module.check_mode:
        return result

    ssh_ip = module.params['ssh_ip']
    ssh_port = module.params['ssh_port']
    ssh_user = module.params['ssh_user']
    ssh_password = module.params['ssh_password']
    ip = module.params['ip']
    port = module.params['port']
    user = module.params['user']
    password = module.params['password']
    db = module.params['db']
    cmmds = module.params['cmmds']
    if_auth = module.params['if_auth']
    timeout = module.params['timeout']

    jump_server = SSHTunnelForwarder(
        ssh_address_or_host=(ssh_ip, ssh_port),
        ssh_username=ssh_user,
        ssh_password=ssh_password,
        remote_bind_address=(ip, port))

    jump_server.daemon_forward_servers = True
    jump_server.daemon_transport = True
    jump_server.start()

    bind_port = jump_server.local_bind_port

    conn_dict = {}
    if if_auth == 'yes':
        conn_dict = {
            'username': user,
            'password': password
        }

    r_session = StrictRedis(host='127.0.0.1',
                            port=bind_port,
                            db=db,
                            **conn_dict,
                            encoding='UTF-8', charset='UTF-8',
                            decode_responses=True,
                            socket_timeout=timeout)

    try:
        r_session.client_setname(ansible_session_flag)
        r_result = r_session.execute_command(cmmds)
        result['message'] = r_result
        result['changed'] = True
        module.exit_json(**result)
    except Exception as err:
        errmsg = str(err)
        result['message'] = f"Cmmds {cmmds} execute failed,failed message is\n{errmsg}"
        module.fail_json(msg=str(err), **result)
    finally:
        r_session.close()
        jump_server.stop(force=True)


def main():
    run_module()


if __name__ == '__main__':
    main()
